<template>
  <div class="enhanced-transfer">
    <!-- 左侧面板 -->
    <div class="transfer-panel">
      <div class="panel-header">
        <slot name="left-header">
          <div class="panel-header-left">
            <el-checkbox
              v-model="checkAllLeft"
              :indeterminate="isIndeterminateLeft"
              @change="handleCheckAllChangeLeft"
            ></el-checkbox>
            <span style="margin-left: 5px">{{ leftTitle }}</span>
          </div>
          <div class="panel-header-right">
            {{ leftChecked.length }}/{{ filteredLeftData.length }}
          </div>
        </slot>
      </div>
      <div class="panel-filters" v-if="filterable">
        <slot name="left-filters-com"></slot>
        <el-input
          v-for="(filter, index) in leftFilters"
          :key="'left-filter-' + index"
          v-model="leftFilterValues[index]"
          :placeholder="filter.placeholder"
          clearable
          size="small"
          @change="handleLeftFilterChange"
        />
      </div>
      <div class="panel-body" v-if="filteredLeftData.length">
        <el-checkbox-group v-model="leftChecked" @change="changeNameLeft">
          <el-checkbox
            v-for="item in filteredLeftData"
            :key="item[keyProp]"
            :label="item[keyProp]"
            :disabled="item[disabled]"
            class="transfer-item"
          >
            <slot name="left-item" :item="item">
              {{ item[labelProp] }}
            </slot>
          </el-checkbox>
        </el-checkbox-group>
      </div>
      <div v-else class="panel-body">
        <!-- <el-empty :image-size="60" description="No data" /> -->
        <slot name="left-empty"></slot>
      </div>
    </div>

    <!-- 中间操作按钮 -->
    <div class="transfer-buttons">
      <el-space :direction="direction" :wrap="menuWrap">
        <el-button
          v-if="!buttonDisplay[0] || buttonDisplay[0] == 'true'"
          type="primary"
          :disabled="rightChecked.length === 0"
          @click="moveToLeft"
        >
          <el-icon><ArrowLeftBold /></el-icon>
          {{ buttonTexts[0] }}
        </el-button>
        <el-button
          v-if="!buttonDisplay[1] || buttonDisplay[1] == 'true'"
          type="primary"
          :disabled="leftChecked.length === 0"
          @click="moveToRight"
        >
          <el-icon><ArrowRightBold /></el-icon>
          {{ buttonTexts[1] }}
        </el-button>

        <slot name="custom-buttons"></slot>
      </el-space>
    </div>

    <!-- 右侧面板 -->
    <div class="transfer-panel">
      <div class="panel-header">
        <slot name="right-header">
          <div class="panel-header-left">
            <el-checkbox
              v-model="checkAllRight"
              :indeterminate="isIndeterminateRight"
              @change="handleCheckAllChangeRight"
            ></el-checkbox>
            <span style="margin-left: 5px">{{ rightTitle }}</span>
          </div>
          <div class="panel-header-right">
            {{ rightChecked.length }}/{{ rightData.length }}
          </div>
        </slot>
      </div>
      <div class="panel-filters" v-if="filterable">
        <slot name="right-filters-com"> </slot>
        <el-input
          v-for="(filter, index) in rightFilters"
          :key="'right-filter-' + index"
          v-model="rightFilterValues[index]"
          :placeholder="filter.placeholder"
          clearable
          size="small"
          @change="handleRightFilterChange"
        />
      </div>
      <div class="panel-body" v-if="filteredRightData.length">
        <el-checkbox-group v-model="rightChecked" @change="changeNameRight">
          <el-checkbox
            v-for="item in filteredRightData"
            :key="item[keyProp]"
            :label="item[keyProp]"
            class="transfer-item"
          >
            <slot name="right-item" :item="item">
              {{ item[labelProp] }}
            </slot>
          </el-checkbox>
        </el-checkbox-group>
      </div>
      <div class="panel-body" v-else>
        <slot name="right-empty"></slot>
      </div>
    </div>
  </div>
</template>


<script lang="ts" setup>
const props = defineProps({
  // 数据源
  data: {
    type: Array,
    default: () => [],
  },
  // 已选择的数据(右侧数据)
  modelValue: {
    type: Array,
    default: () => [],
  },
  // 键名属性
  keyProp: {
    type: String,
    default: "key",
  },
  disabled: {
    type: String,
    default: "disabled",
  },
  // 显示文本属性
  labelProp: {
    type: String,
    default: "label",
  },
  // 左侧标题
  leftTitle: {
    type: String,
    default: "未选",
  },
  // 右侧标题
  rightTitle: {
    type: String,
    default: "已选",
  },
  // 左侧筛选条件配置
  leftFilters: {
    type: Array,
    default: () => [{ placeholder: "请输入筛选条件" }],
  },
  // 右侧筛选条件配置
  rightFilters: {
    type: Array,
    default: () => [{ placeholder: "请输入筛选条件" }],
  },
  // 自定义筛选函数
  filterMethod: {
    type: Function,
    default: null,
  },
  // 自定义按钮文字
  buttonTexts: {
    type: Array,
    default: () => ["", ""],
  },
  // 是否显示自带的按钮组
  buttonDisplay: {
    type: Array,
    default: () => ["true", "true"],
  },
  // 操作按钮换行
  menuWrap: {
    type: Boolean,
    default: false,
  },
  // 按钮布局方式
  direction: {
    type: String,
    default: "horizontal", // vertical(垂直) | horizontal(平行)
  },
  // 开启搜索过滤
  filterable: {
    type: Boolean,
    default: false,
  },
  // 默认选中左侧列表数据
  leftDefaultChecked: {
    type: Array,
    default: () => [],
  },
  // 默认选中右侧列表数据
  rightDefaultChecked: {
    type: Array,
    default: () => [],
  },
});

const {
  data,
  modelValue,
  keyProp,
  labelProp,
  leftTitle,
  rightTitle,
  leftFilters,
  rightFilters,
  filterMethod,
  buttonTexts,
  menuWrap,
  direction,
  filterable,
  buttonDisplay,
  disabled,
  leftDefaultChecked,
  rightDefaultChecked,
} = toRefs(props);

const emit = defineEmits();

const checkAllLeft = ref(false);
const isIndeterminateLeft = ref(false);

const checkAllRight = ref(false);
const isIndeterminateRight = ref(false);

const leftFilterValues = ref([]); // 左侧筛选值
const rightFilterValues = ref([]); // 右侧筛选值

const handleCheckAllChangeLeft = (val) => {
  leftChecked.value = val ? data.value.map((item) => item[keyProp.value]) : [];
  isIndeterminateLeft.value = false;
};
function changeNameLeft(val) {
  const checkedCount = val.length;
  checkAllLeft.value = checkedCount === filteredLeftData.value.length;
  isIndeterminateLeft.value =
    checkedCount > 0 && checkedCount < filteredLeftData.value.length;
}

const handleCheckAllChangeRight = (val) => {
  rightChecked.value = val
    ? filteredRightData.value.map((item) => item[keyProp.value])
    : [];
  isIndeterminateRight.value = false;
};
function changeNameRight(val) {
  const checkedCount = val.length;
  checkAllRight.value = checkedCount === filteredRightData.value.length;
  isIndeterminateRight.value =
    checkedCount > 0 && checkedCount < filteredRightData.value.length;
}

// 右侧数据(已选数据)
const rightData = computed({
  get() {
    return modelValue.value;
  },
  set(value) {
    emit("update:modelValue", value);
  },
});

// 左侧数据(总数据减去已选数据)
const leftData = computed(() => {
  return data.value.filter(
    (item) =>
      !rightData.value.some(
        (selectedItem) => selectedItem[keyProp.value] === item[keyProp.value]
      )
  );
});

// 筛选后的左侧数据
const filteredLeftData = computed(() => {
  if (filterMethod.value) {
    return filterMethod.value(leftData.value, leftFilterValues.value, "left");
  } else {
    return defaultFilter(leftData.value, leftFilterValues.value);
  }
});

// 筛选后的右侧数据
const filteredRightData = computed(() => {
  if (filterMethod.value) {
    return filterMethod.value(
      rightData.value,
      rightFilterValues.value,
      "right"
    );
  }
  return defaultFilter(rightData.value, rightFilterValues.value);
});

const comLeftDefaultChecked = computed(() => {
  if (leftDefaultChecked.value.length) {
    const checkedCount = leftDefaultChecked.value.length;
    checkAllLeft.value = checkedCount === filteredLeftData.value.length;
    isIndeterminateLeft.value =
      checkedCount > 0 && checkedCount < filteredLeftData.value.length;
    return leftDefaultChecked.value;
  } else {
    return [];
  }
});

const comRightDefaultChecked = computed(() => {
  if (rightDefaultChecked.value.length) {
    const checkedCount = rightDefaultChecked.value.length;

    checkAllRight.value = checkedCount === filteredRightData.value.length;
    isIndeterminateRight.value =
      checkedCount > 0 && checkedCount < filteredRightData.value.length;
    return rightDefaultChecked.value;
  } else {
    return [];
  }
});

const leftChecked = ref(comLeftDefaultChecked.value); // 左侧选中项

const rightChecked = ref(comRightDefaultChecked.value); // 右侧选中项

// 默认筛选方法
function defaultFilter(data, filterValues) {
  if (!filterValues || filterValues.every((val) => !val)) {
    return data;
  }
  return data.filter((item) => {
    return filterValues.every((filterValue) => {
      if (!filterValue) return true;
      return Object.values(item).some((val) =>
        String(val).toLowerCase().includes(filterValue.toLowerCase())
      );
    });
  });
}

// 左侧筛选变化
function handleLeftFilterChange() {
  leftChecked.value = [];
}
// 右侧筛选变化
function handleRightFilterChange() {
  rightChecked.value = [];
}

// 移动到右侧
function moveToRight() {
  const movedItems = leftData.value.filter((item) =>
    leftChecked.value.includes(item[props.keyProp])
  );
  emit("input", [...rightData.value, ...movedItems]);
  emit("change", [...rightData.value, ...movedItems], "right", movedItems);
  rightData.value = [...rightData.value, ...movedItems];
  leftChecked.value = [];
  checkAllLeft.value = false;
  isIndeterminateLeft.value = false;
}
// 移动到左侧
function moveToLeft() {
  const remainingItems = rightData.value.filter(
    (item) => !rightChecked.value.includes(item[keyProp.value])
  );
  emit("input", remainingItems);
  emit(
    "change",
    remainingItems,
    "left",
    rightData.value.filter((item) =>
      rightChecked.value.includes(item[keyProp.value])
    )
  );

  rightData.value = remainingItems;
  rightChecked.value = [];
  checkAllRight.value = false;
  isIndeterminateRight.value = false;
}

defineExpose({
  moveToLeft,
  moveToRight,
});
</script>
 
<style scoped lang="scss">
.enhanced-transfer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.transfer-panel {
  width: 40%;
  height: 420px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
}

.panel-header {
  height: 40px;
  line-height: 40px;
  background: #f5f7fa;
  padding: 0 10px;
  border-bottom: 1px solid #ebeef5;
  color: #333;
  display: flex;
  justify-content: space-between;
  &-left {
    display: flex;
    align-items: center;
    font-weight: bold;
  }
  &-right {
    color: #909399;
    font-size: 12px;
  }
}

.panel-filters {
  padding: 10px;
  border-bottom: 1px solid #ebeef5;
}

.panel-filters .el-input {
  margin-bottom: 8px;
}

.panel-filters .el-input:last-child {
  margin-bottom: 0;
}

.panel-body {
  height: 300px;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 10px;
}

.transfer-item {
  width: 100%;
}

.transfer-buttons {
  display: flex;
  align-items: center;
  padding: 0 20px;
}

.transfer-buttons .el-button {
  margin: 5px;
}

.custom-buttons {
  margin-top: 15px;
  display: flex;
  flex-direction: column;
}
</style>